/**
 * @file State.c
 * @author lhc (liuhc_lhc@163.com)
 * @brief 状态机执行函数
 * @version 0.2
 * @date 2024-01-28
 *
 * @copyright Copyright (c) 2024
 *
 * @par Change History
 *  Date        Version     Author    Description
 *  ----------  ----------  -------   -----------------------------
 *  2024-01-28  0.1         lhc       初始版本
 *  2024-06-22  0.2         lhc       添加FsmProcessEvents函数,对于外部调用函数进行空指针检查
 */

#include "FsmState.h"
#include <stdio.h>

#if INCLUDE_FIFO
#include "Fifo.h"
#endif

/**
 * @brief 初始化状态函数
 *
 * @param pSM 状态机
 * @param pStateTable 状态表
 * @param ucMax 支持最大状态数量
 * @param enEvt 初始状态
 */
void FsmInit(STR_STATE_MACHINE *pSM, STR_STATE_INFO *pStateTable,
             const uint8_t ucMax, const ENUM_STATE_T enEvt)
{
    if (pSM == NULL || pStateTable == NULL) {
        return;
    }
    pSM->usStateNum = 0;
    pSM->enCurState = enEvt;
    pSM->pStateTable = pStateTable;
    pSM->ucMax = ucMax;
}

/**
 * @brief 查表函数
 *
 * @param pSM 当前实时状态结构体
 * @param enEvt 事件id
 * @return const STR_STATE_INFO * 对应事件和状态的地址
 */
static const STR_STATE_INFO *FsmFindAddr(STR_STATE_MACHINE *pSM, const ENUM_EVENT_T enEvt)
{
    const STR_STATE_INFO *pStateTable = pSM->pStateTable;
    for (uint8_t i = 0; i < pSM->usStateNum; i++)
    {
        if (((pStateTable + i)->enCurState == pSM->enCurState) &&
            ((pStateTable + i)->enEventId == enEvt))
        {
            return &pStateTable[i];
        }
    }
    return NULL;
}

/**
 * @brief 运行状态机
 *
 * 根据传入的事件和状态机指针，执行状态机的运行逻辑，进行状态转移和执行对应的状态函数。
 *
 * @param pSM 状态机指针
 * @param enEvt 事件枚举类型
 */
void FsmMachineRun(STR_STATE_MACHINE *pSM, const ENUM_EVENT_T enEvt)
{
    if (pSM == NULL) {
        return;
    }
    const STR_STATE_INFO *pStStateInfo = NULL;
    pStStateInfo = FsmFindAddr(pSM, enEvt);

    if (pStStateInfo != NULL) // 判断是否有这个状态
    {
        pSM->enCurState = pStStateInfo->enNextState; // 状态转移 可以动态转移 比如在时间执行函数中进行转移
        if (pStStateInfo->pStateFunc)
        {
            pStStateInfo->pStateFunc(enEvt);
        }
    }
}

/**
 * @brief 添加任务
 *
 * @param pStateBuf 状态表指针
 * @param ucMax 最大任务数量
 * @param enCurState 当前状态
 * @param enEventId 事件id
 * @param enNextState 下一个状态
 * @param pAction 事件函数
 * @return uint8_t 是否添加成功
 */
bool FsmStateAdd(STR_STATE_MACHINE *pStateBuf, ENUM_STATE_T enCurState,
                 ENUM_EVENT_T enEventId, ENUM_STATE_T enNextState, VoidUint8FuncPtr pAction)
{
    if (pStateBuf == NULL) {
        return false;
    }
    STR_STATE_INFO *pStateInfo = NULL;
    if (pStateBuf->usStateNum >= pStateBuf->ucMax - 1)
        return false;

    pStateInfo = (STR_STATE_INFO *)&(pStateBuf->pStateTable[pStateBuf->usStateNum]);

    pStateInfo->enCurState = enCurState;
    pStateInfo->enEventId = enEventId;
    pStateInfo->enNextState = enNextState;
    pStateInfo->pStateFunc = pAction;
    ++pStateBuf->usStateNum;

    return true;
}

#if INCLUDE_FIFO
/**
 * @brief 处理状态机事件
 *
 * 处理状态机中的事件，从给定的环形缓冲区中读取事件并处理它们。
 *
 * @param pSM 状态机指针
 * @param pFifo 环形缓冲区指针
 * @param maxCount 最大处理事件数量 默认最大处理次数为 DEFAULT_MAX
 * @note 不建议使用这个这个因为如果使这个需要引入环形缓冲区，这样代码耦合度高，移植复杂
 */
void FsmProcessEvents(STR_STATE_MACHINE* pSM, void* pFifo,
                      uint8_t maxCount)
{
    if (pSM == NULL || pFifo == NULL) {
        return;
    }
    ENUM_EVENT_T evt = 0;
    maxCount         = maxCount == 0 ? DEFAULT_MAX : maxCount;

    while (!ring_buffer_is_empty(pFifo) && maxCount > 0) {
        if (ring_buffer_dequeue(pSM, (uint8_t*)&evt) != 0) {
            FsmMachineRun(pSM, (ENUM_EVENT_T)evt);
        }
    }
}
#else
/**
 * @brief 处理状态机事件
 *
 * 从事件队列中取出事件，并调用状态机运行函数处理事件。
 *
 * @param pSM 状态机指针
 * @param pFifo 事件队列指针
 * @param getEventFunc 获取事件函数指针
 * @param maxCount 最大事件处理次数 默认最大处理次数为 DEFAULT_MAX
 */
void FsmProcessEvents(STR_STATE_MACHINE* pSM, void* pFifo,
                      FsmGetEventFunc getEventFunc, uint8_t maxCount)
{
    if (pSM == NULL || pFifo == NULL || getEventFunc == NULL) {
        return;
    }
    maxCount         = maxCount == 0 ? DEFAULT_MAX : maxCount;
    ENUM_EVENT_T evt = 0;

    while (getEventFunc((void*)pFifo, &evt) != 0 && maxCount-- > 0) {
        FsmMachineRun(pSM, (ENUM_EVENT_T)evt);
    }
}
#endif